/**
 * 
 */
package com.I9lou.se.lurker.remote.support;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Similarity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.I9lou.se.lurker.facet.index.DocumentFacetBuilder;
import com.I9lou.se.lurker.index.IndexOptimizer;
import com.I9lou.se.lurker.index.IndexWriterFactory;
import com.I9lou.se.lurker.remote.IndexWriterService;
import com.I9lou.se.lurker.remote.IndexableDocument;

/**
 * 索引更新服务实现类
 * @author chenwei
 * @date 2011-03-07
 * @ThreadSafe
 */
public class SimpleIndexWriterService implements IndexWriterService {
	
	private DocumentFacetBuilder documentFacetBuilder = null;
	
	public void setDocumentFacetBuilder(DocumentFacetBuilder documentFacetBuilder) {
		this.documentFacetBuilder = documentFacetBuilder;
	}

	/**
	 * 日志输出
	 */
	private static Logger LOG = LoggerFactory.getLogger(SimpleIndexWriterService.class);
	
	/**
	 * IndexWriter实例工厂
	 */
	private IndexWriterFactory indexWriterFactory = null;
	
	/**
	 * 索引优化器
	 */
	private IndexOptimizer indexOptimizer = null;
	
	private Similarity similarity = null;
	
	/**
	 * 索引更新服务可提供操作类型枚举类
	 */
	private static enum ExecuteType {
		ADD, UPDATE, DELETE
	}

	/**
	 * 设置IndexWriter实例工厂
	 * @param indexWriterFactory
	 */
	public void setIndexWriterFactory(IndexWriterFactory indexWriterFactory) {
		this.indexWriterFactory = indexWriterFactory;
	}

	/**
	 * 设置索引优化器，用于查询指定目录是否正在执行优化
	 * @param indexOptimizer
	 */
	public void setIndexOptimizer(IndexOptimizer indexOptimizer) {
		this.indexOptimizer = indexOptimizer;
	}

	public void setSimilarity(Similarity similarity) {
		this.similarity = similarity;
	}

	@Override
	public Response addDocument(String indexDirName, IndexableDocument doc) {
		return execute(ExecuteType.ADD, indexDirName, new Object[]{doc});
	}

	@Override
	public Response deleteDocument(String indexDirName, Term term) {
		return execute(ExecuteType.DELETE, indexDirName, new Object[]{term});
	}

	@Override
	public boolean updateDocument(int appId,Collection<String> primaryKeys, List<Map<String, Object>> dataList){
		for (Map<String, Object> data : dataList) {
			String docKeyValue = data.get("__doc_key").toString();
			try {
				String indexDirName = data.get("indexDirName").toString();
				IndexableDocument doc = (IndexableDocument) data.get("doc");
				Term term = new Term("__doc_key", docKeyValue);
				Response rep = execute(ExecuteType.UPDATE, indexDirName, new Object[] { term, doc });
				if (LOG.isDebugEnabled())
					LOG.debug("[Build]Status: {}, docKeyValue: {}", rep, docKeyValue);
				if (rep != Response.OK) {
					LOG.warn("[Build]Failed response: {}; docKey: {}; ", new Object[] { term, doc });
					return false; // 如果索引在一台机器上创建失败，则返回false
				}
			} catch (Exception e) {
				LOG.error("docKey: " + docKeyValue, e);
				return false;
			}

		}
		return true;
	}
	
	@Override
	public boolean deleteDocuments(int appId,Collection<String> primaryKeys, List<Map<String, Object>> dataList){
		for(Map<String,Object> data:dataList){
			String indexDirName = data.get("indexDirName").toString();
			String docKeyValue = data.get("__doc_key").toString();
			IndexableDocument doc = (IndexableDocument) data.get("doc");
			Term term = new Term("__doc_key", docKeyValue);
			Response rep = execute(ExecuteType.DELETE, indexDirName, new Object[] { term, doc });
			if( rep != Response.OK ) {
				LOG.warn("[Build]Failed response: {}; docKey: {}; ", new Object[]{ rep, docKeyValue});
				return false; //如果索引在一台机器上创建失败，则返回false
			}
		}
		return true;
	}
	//end  edit
	
	@SuppressWarnings("deprecation")
	private Response execute(ExecuteType type, String indexDirName, Object[] paras) {
		IndexWriter writer = indexWriterFactory.get(indexDirName);
		if( writer != null ) {
			writer.setSimilarity(similarity);
			if( !indexOptimizer.isOptimizing(indexDirName) ) { //判断该索引是否正在进行优化
				try {
					switch( type ) {
						case ADD:
							doAdd(writer, paras);
							break;
						case UPDATE:
							doUpdate(writer, paras);
							break;
						case DELETE:
							doDelete(writer, paras);
							break;
						default:
							break;
					}
					return Response.OK;
				} catch(OutOfMemoryError e) {
					indexWriterFactory.close(indexDirName);
					LOG.error(type + " error! IndexDirName: " 
							  + indexDirName + ", Parameters: " 
							  + Arrays.toString(paras), e);
					return Response.ERROR;
				} catch (Exception e) {
					LOG.error(type + " error! IndexDirName: " 
							  + indexDirName + ", Parameters: " 
							  + Arrays.toString(paras), e);
					return Response.ERROR;
				} 
			} else {
				return Response.OPTIMIZE;
			}
		} else {
			return Response.UNAVAILABLE;
		}
	}
	
	private void doAdd(IndexWriter writer, Object[] paras) throws CorruptIndexException, IOException {
		IndexableDocument indexableDocument = (IndexableDocument)paras[0];
		Document document = DocumentConverter.convert(indexableDocument);
		addFacetIfNeed(indexableDocument, document);
		writer.addDocument( document );
	}

	private void doUpdate(IndexWriter writer, Object[] paras) throws CorruptIndexException, IOException {
		Term term = (Term) paras[0];
		IndexableDocument indexableDocument = (IndexableDocument)paras[1];
		Document document = DocumentConverter.convert(indexableDocument);
		addFacetIfNeed(indexableDocument, document);
		writer.updateDocument(term, document);
	}
	
	private void doDelete(IndexWriter writer, Object[] paras) throws CorruptIndexException, IOException {
		Term term = (Term) paras[0];
		writer.deleteDocuments(term);
	}
	
	private void addFacetIfNeed(IndexableDocument indexableDocument,
								Document document) {
		if( indexableDocument.isFacetEnabled() ){
			documentFacetBuilder.build(document, indexableDocument.getFacets());
		}
	}
	
}
